#pragma once
#include "IMLObjects.h"


namespace IcyCrystal {
	

	class IMLGenerator {
	private:
		Namespace& globalNamespace;
		SyntaxTreeList& syntaxTree;

		ArgList* genGlobals(_symbolMap& symbolMap);

		GlobalDeclaration createArrayDeclaration(Symbol* sym);
		vector<GlobalDeclaration>* constToDeclaration(SyntaxTree* tree);

		ArgType scopeToArgType(Symbol* sym);

		// Functions
		ArgList* genFunctions();
		ArgList* generateFunction(Symbol* funcSymbol);
		void setupFunctionParameters(Symbol* funcSymbol);
		/** Initiates the local position, and returns the total size of locals */
		int setupFunctionLocals(Symbol* funcSymbol);

		// Statments & Expressions
		ArgList* generateExpression( Namespace* currentNamespace, SyntaxTree* expr, Arg& finalDest);
		ArgList* generateStatement(Namespace* currentNamespace, SyntaxTree* statement);
		ArgList* generateFunctionBody(Symbol* funcSymbol);

		// Registers
		Register* registerArray;
		ArgList* getRegister(Symbol* sym, Quad& statement, UseMap& map,int statementNumber, int& out);
		int getRegisterFromSymbol(Symbol* sym);
		int getEmptyRegister();
		void clearAllRegisters();
		
		bool symbolIsInRegister(Symbol* sym, int reg );
		void removeRegisterFromAllSymbols(Namespace* currentNamespace, int reg, Symbol* except);
		void removeRegisterFromSymbol( Symbol* sym, int reg);

		// Second pass
		UseMap* computeLastUse(Namespace* currentNamespace, ArgList* block);
		inline void computeLastUseHelper(Namespace* currentNamespace, Arg& arg, UseMap& map, int i, int x);
		Symbol* createTempSymbol(Namespace* currentNamespace, Arg& arg);
		Quad* generateLoadQuad(Symbol* sym, int reg);
		Quad* generateStoreQuad(Symbol* sym, int reg);
		/** A generic command is a command like ADD, where the second argument is also the target */
		ArgList* generateGenericQuad(Namespace* currentNamespace, int currentStatement, UseMap &map, OpType op, int lReg, int rReg, Symbol* lSym, Symbol* rSym, Symbol* t, Arg& argT);
		Quad* generateCopyQuad(Namespace* currentNamespace, int rReg, Symbol* lSym);
		ArgList* doSecondPass( Namespace* currentNamespace, ArgList* block);
		ArgList* generateEndBlock(Namespace* currentNamespace);

		// Temporaries
		int temporaryCount;
		Arg getNewTemporary();
		void freeLastTemporary();
	public:

		IMLGenerator(Namespace& globalNamepace, SyntaxTreeList& syntaxTree);
		void generateIML();
	};
}
